/*
* "doctothumb" program converts the page(s) of a document into jpeg image files
* 
* Copyright (C) 2013 Alin Dumitrescu
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.

* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*
* Distributed under the GNU GPL v2. For full terms see the file Copying or
* License URL at: http://www.gnu.org/licenses/gpl.html GPL version 2
*/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "aconf.h"
#include "../xpdf-3.03/goo/parseargs.h"
#include "../xpdf-3.03/goo/gmem.h"
#include "../xpdf-3.03/xpdf/GlobalParams.h"
#include "../xpdf-3.03/xpdf/Object.h"
#include "../xpdf-3.03/xpdf/config.h"
#include "../xpdf-3.03/xpdf/SplashOutputDev.h"
#include "doctothumb.h"

HWND hWnd;
HWND hWndParent;
char szTitle[] = "DOC2Thumb";
char szWindowClass[] = "CWDOCTOTHUMB";

int volatile MaxThreads;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    srand((unsigned int)time( NULL ));
    DWORD ProcessMask, SystemMask;
    MaxThreads = 1;
    if (GetProcessAffinityMask( GetCurrentProcess(), &ProcessMask, &SystemMask))
        while ((ProcessMask = (ProcessMask >> 1)) > 0)
            MaxThreads += (ProcessMask & 0x00000001U);
    MaxThreads *= 2;
    globalParams=NULL;
    if (!sscanf_s(lpCmdLine, "%d", &hWndParent))                         //the first parameter is the parent HWND
        return 1;                                                        //no parameter, no parent, no application
    app_register(hInstance);
    if (!InitInstance (hInstance, nCmdShow))
        return 1;
    ProcessLoop();
    if(globalParams)
        delete globalParams;                                             //if application was successful, destroy the global parameters
    return 0;                                                            //return no error
}

ATOM app_register(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize            = sizeof(WNDCLASSEX);
    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = 0;
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = NULL;
    wcex.lpszClassName    = szWindowClass;
    wcex.hIconSm        = 0;
    return RegisterClassEx(&wcex);                                       //simplistic wnd class declaration, no fancy stuff
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    if (!hWnd)
        return FALSE;

    const char* fonts_dir_name="\fonts";                                 //the "fonts" directory
    char fonts_dir[MAX_PATH];
    GetModuleFileName(0,fonts_dir,MAX_PATH);                             //must be a subdirectory of the application
    char* exename=strrchr(fonts_dir,'\\');
    if (exename)
        strcpy(exename,fonts_dir_name);

    globalParams = new GlobalParams(NULL);                               //create the global parameters object
    globalParams->setAntialias("yes");                                   //use antialias
    globalParams->setVectorAntialias("yes");                             //use vector antialias
    globalParams->setEnableT1lib("no");                                  //dont use this,
    globalParams->setEnableFreeType("yes");                              //use free type instead
    if (exename)
        globalParams->setupBaseFonts(fonts_dir);                         //set the "fonts" directory
    globalParams->setErrQuiet(gTrue);                                    //dont write errors to stdout/stderr

    SCMMono[0]=0xFF;                                                     //initialization
    SCMColor[0]=SCMColor[1]=SCMColor[2]=0xFF;                            //of the background paper

    ShowWindow(hWnd,nCmdShow);
    UpdateWindow(hWnd);
    InvalidateRect(hWnd,NULL,1);
    PostMessage( hWndParent, WM_USER, WP_APPLICATION_ACK, (LPARAM)hWnd); //tell the parent application that we are ready
    return TRUE;
}

void ProcessLoop()
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT lResult = 0;
    PDOC_Context pCtx = NULL;
    PCOPYDATASTRUCT pCD = NULL;
    switch (message)
    {
    case WM_MSG_APP:
        switch(wParam)
        {
        case WP_CREATE_PDF_CTX:
            pCtx = (PPDF_Context)malloc(sizeof(PDF_Context));            //create GRBJ, LPARAM = XX0YZZZ
            pCtx->Resolution = (lParam & 0x0000FFFF);                    //set DPI (the ZZZZ part of the LPARAM)
            ((PPDF_Context)pCtx)->cm =
                (SplashColorMode)((lParam & 0x000F0000) >> 16);          //get the "int mode" (the Y part of the LPARAM: 0 ,1   ,2  ,3)
                                                                         //and convert it to "Color Mode":               BW,GRAY,RGB,BGR
            ((PPDF_Context)pCtx)->pSC =
                ((PPDF_Context)pCtx)->cm < splashModeRGB8 ?
                    &SCMMono : &SCMColor;                                //set a background color with respect to the "Color Mode"

            pCtx->Quality = ((lParam & 0xFF000000) >> 24);               //set JPEG Quality (the XX part of the LPARAM)
            lResult = (LRESULT)pCtx;                                     //use the message return code for the 
                                                                         //GRBJ pointer address sent to the parent for later identification
            break;
        case WP_CREATE_IMG_CTX:
            pCtx = (PIMG_Context)malloc(sizeof(IMG_Context));            //create GRBJ, LPARAM = XX0YZZZ
            pCtx->Resolution = (lParam & 0x0000FFFF);                    //set DPI (the ZZZZ part of the LPARAM)
            ((PIMG_Context)pCtx)->BPP = 1;
            switch((lParam & 0x000F0000) >> 16)                          //get the "int mode" (the Y part of the LPARAM: 0 ,1   ,2)
            {                                                            //and convert it to "Color Mode":               BW,GRAY,RGB
            default:
            case 2:
                ((PIMG_Context)pCtx)->BPP += 16;
            case 1:
                ((PIMG_Context)pCtx)->BPP += 7;
            case 0:;
            }
            pCtx->Quality = ((lParam & 0xFF000000) >> 24);               //set JPEG Quality (the XX part of the LPARAM)
            lResult = (LRESULT)pCtx;                                     //use the message return code for the 
                                                                         //GRBJ pointer address sent to the parent for later identification
            break;
        case WP_DESTROY_CTX:
            if(lParam)
                free((void*)lParam);                                     //free GRBJ
            lResult = 0;
            break;
        }
        break;
    case WM_COPYDATA:
        pCD = (PCOPYDATASTRUCT)lParam;
        switch(pCD->dwData)
        {
        case WP_START_PDF_RENDER:
            PDFProcessStartRender((PCommandRender)pCD->lpData);
            lResult = WP_START_PDF_RENDER;
            break;
        case WP_START_IMG_RENDER:
            IMGProcessStartRender((PCommandRender)pCD->lpData);
            lResult = WP_START_IMG_RENDER;
            break;
        }
        break;
    case WM_CLOSE:
        DestroyWindow( hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return lResult;
}

void PostResult( int AErrorCode, PCommandRender pCR, char* Files)
{
    COPYDATASTRUCT CDS;
    CDS.dwData = WP_RENDER_DONE;
    if(AErrorCode < 1)
    {
        ResultRender RR;
        RR.Pages = AErrorCode;                                           //the error code
        RR.pContext = pCR->pContext;                                     //GRBJ for which the error occured
        RR.OutFilenames[ 0] = 0x00;                                      //no output files
        CDS.cbData = sizeof(ResultRender);
        CDS.lpData = (void*)&RR;
        SendMessage(hWndParent,WM_COPYDATA,(WPARAM)hWnd,(LPARAM)&CDS);
    }
    else
    {
        int datalen = strlen(Files);
        int datasize = sizeof(ResultRender) + datalen;                   //sizeof(ResultRender) is already one char bigger, so no need for an extra char (the NULL)
        PResultRender pRR = (PResultRender)malloc(datasize);
        pRR->Pages = pCR->Pages;                                         //page count of the document
        pRR->pContext = pCR->pContext;                                   //GRBJ for which the task is over
        pRR->OutFilenames[ datalen] = 0x00;                              //set the NULL char at the end of the buffer
        CopyMemory( &pRR->OutFilenames[0], Files, datalen);              //buffer to buffer
        CDS.cbData = datasize;
        CDS.lpData = (void*)pRR;
        SendMessage(hWndParent,WM_COPYDATA,(WPARAM)hWnd,(LPARAM)&CDS);
        free((void*)pRR);                                                //free the posted data
    }
}

void CreateUniqueFile( unsigned char charlen, char* Path, char* Filename)       //self explanatory function
{
    const char * CharMap = "qwertzuiopasdfghjklyxcvbnm1234567890";
    const int CharMapLen = 36;
    int pathlen = strlen(Path);
    int strlen;
    int file = -1;
    while(file == -1)
    {
        strlen = pathlen;
        strcpy( Filename, Path);
        if(Path[pathlen - 1] != '\\')
        {
            strlen++;
            strcat(Filename,"\\");                                              //add trailing path delimiter
        }
        for(int i = 0; i < charlen; i++)
        {
            int rnd = rand();
            int pos = (int)((double)(rnd * CharMapLen) / (RAND_MAX + 1));
            Filename[strlen++] = CharMap[pos];                                  //add random chars
        }
        Filename[strlen] = '\00';
        strcat(Filename,".jpg");
        file = _open( Filename, _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);      //create the file
    };
    _close(file);                                                               //close the handle
}